package handler

import (
	"context"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"service_base_shop/jwt_srv/global"
	"service_base_shop/jwt_srv/model"
	"service_base_shop/jwt_srv/proto"
	"time"
)

type JWT struct {
	SigningKey []byte
}

var (
	TokenExpired     = status.Errorf(codes.NotFound, "Token is expired")
	TokenNotValidYet = status.Errorf(codes.NotFound, "Token not active yet")
	TokenMalformed   = status.Errorf(codes.NotFound, "That's not even a token")
	TokenInvalid     = status.Errorf(codes.NotFound, "Couldn't handle this token:")
)

func NewJWT() *JWT {
	return &JWT{
		[]byte(global.ServerConfig.JWTConfig.SigningKey), //可以设置过期时间
	}
}

type Jwt struct {
}

func (j *JWT) CreateToken(claims model.CustomClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(j.SigningKey)
}

func (j *JWT) ParseToken(TokenString string) (*model.CustomClaims, error) {
	token, err := jwt.ParseWithClaims(TokenString, &model.CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) {
		return j.SigningKey, nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			fmt.Println(ve.Errors)
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, TokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, TokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, TokenNotValidYet
			} else {
				return nil, TokenInvalid
			}
		}
	}
	if token != nil {
		if claims, ok := token.Claims.(*model.CustomClaims); ok && token.Valid {
			return claims, nil
		}
		return nil, TokenInvalid
	} else {
		return nil, TokenInvalid
	}
}
//// 更新token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}
	token, err := jwt.ParseWithClaims(tokenString, &model.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	fmt.Println(err)
	if err != nil {
		return "", status.Errorf(codes.NotFound, "更新失败")
	}
	if claims, ok := token.Claims.(*model.CustomClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
		return j.CreateToken(*claims)
	}
	return "", TokenInvalid
}


//创建token
func (this *Jwt) CreateToken(ctx context.Context, CreateTokenReq *protopb.CreateTokenReq) (*protopb.CreateTokenRep, error) {
	j := NewJWT()
	nowtime := time.Now().Unix()
	token, err := j.CreateToken(model.CustomClaims{
		ID:       uint(CreateTokenReq.ID),
		NickName: CreateTokenReq.NickName,
		StandardClaims: jwt.StandardClaims{
			NotBefore: nowtime,
			ExpiresAt: nowtime + 60 * 60 * 24 * 30,
			Issuer:    "shop",
		},
	})
	if err != nil {
		return nil, status.Errorf(codes.NotFound, "创建失败")
	}
	return &protopb.CreateTokenRep{
		Token: token,
	}, nil
}
//解析token
func (this *Jwt) ParseToken(ctx context.Context, ParseTokenReq *protopb.ParseTokenReq) (*protopb.ParseTokenRep, error) {
	j := NewJWT()
	CustomClaims ,err := j.ParseToken(ParseTokenReq.Token)
	if err != nil {
		return nil, err
	}
	return &protopb.ParseTokenRep{
		ID: uint64(CustomClaims.ID),
		NickName: CustomClaims.NickName,
	}, nil
}
//更新token
func (this *Jwt) RefreshToken(ctx context.Context, RefreshTokenReq *protopb.RefreshTokenReq) (*protopb.RefreshTokenRep, error) {
	j := NewJWT()
	token ,err := j.RefreshToken(RefreshTokenReq.Token)
	if err != nil {
		return nil, err
	}
	return &protopb.RefreshTokenRep{
		Token: token,
	}, nil
}


func (this *Jwt) ReduceProduct(ctx context.Context, req *protopb.ReduceProductReq) (*protopb.ReduceProductRep, error) {

	//检查产品是否存在，并修改库存
	product := model.Product{}
	if ret := global.DB.Table("lr_product").Where("id = ? and del = 0 and is_down = 0",req.GoodsId).Select("name,shiyong").Take(&product);ret.RowsAffected == 0 {
		return  nil,status.Errorf(codes.NotFound,"商品未查询到")
	}
	num := product.Num - int(req.Num)
	shiyong := product.Shiyong + int64(req.Num)
	if ret := global.DB.Table("lr_product").Where("id = ?",req.GoodsId).Updates(map[string]interface{}{
		"num":num,
		"shiyong":shiyong,
	}); ret.RowsAffected == 0 {
		return  nil,status.Errorf(codes.NotFound,"更新失败")
	}
	return &protopb.ReduceProductRep{
	}, nil
}

